#ifndef SIMPLE_SUPPORT_ITERATOR_OFFSET_HPP
#define SIMPLE_SUPPORT_ITERATOR_OFFSET_HPP

#include <type_traits> // std::remove_reference_t std::enable_if_t std::is_same_v std::is_reference_v
#include <iterator> // std::iterator_traits std::begin
#include <utility> // std::forward std::move
#include <cassert> // assert

namespace simple::support
{

	template <typename Array>
	// TODO guess gotta constrain to random access or better
	struct offset_iterator
	{
		using offset_type = typename std::remove_reference_t<Array>::size_type; // container type traits? are those existed??
		using iterator_type = typename std::remove_reference_t<Array>::iterator;

		using value_type = typename std::iterator_traits<iterator_type>::value_type;
		using difference_type = typename std::iterator_traits<iterator_type>::difference_type;
		using pointer = typename std::iterator_traits<iterator_type>::pointer;
		using reference = typename std::iterator_traits<iterator_type>::reference;
		using iterator_category = typename std::iterator_traits<iterator_type>::iterator_category;
		Array array;
		offset_type offset;

		template <typename A, std::enable_if_t<std::is_same_v<A,Array>>* = nullptr>
		explicit offset_iterator(A&& array, offset_type offset = 0)
			: array(std::forward<A>(array)), offset(offset)
		{}

		template <typename A, std::enable_if_t<std::is_same_v<A,Array>>* = nullptr>
		explicit offset_iterator(A&& array, iterator_type i)
			: array(std::forward<A>(array)), offset(static_cast<offset_type>(i - array.begin()))
		{}

		offset_iterator(const offset_iterator&) = default;
		offset_iterator(offset_iterator&&) = default;

		// TODO: in c++20 replace with:
		// constexpr
		// offset_iterator& operator=(const offset_iterator& other)
		// 	noexcept(noexcept(std::declval<offset_type> = other.offset))
		// 	requires reference<Array>
		// {
		// 	assert(&array == &other.array);
		// 	offset = other.offset;
		// 	return *this;
		// }
		// offset_iterator& operator=(const offset_iterator&) = default;
		// offset_iterator& operator=(offset_iterator&&) = default;
		offset_iterator& operator=(const offset_iterator& other)
		{
			if constexpr (not std::is_reference_v<Array>)
				array = other.array;
			else
				assert(&array == &other.array);
			offset = other.offset;
		}
		offset_iterator& operator=(offset_iterator&& other)
		{
			if constexpr (not std::is_reference_v<Array>)
				array = std::move(other.array);
			else
				assert(&array == &other.array);
			offset = other.offset;
		}

		friend bool operator==(const offset_iterator& one, const offset_iterator& other)
		{
			return one.base() == other.base();
		}
		friend bool operator!=(const offset_iterator& one, const offset_iterator& other)
		{
			return !(one == other);
		}

		offset_iterator& operator++() { assert(offset < array.size()); ++offset; return *this; }
		offset_iterator operator++(int) { offset_iterator prev = *this; ++(*this); return prev; }
		offset_iterator& operator--() { assert(offset > 0); --offset; return *this; }
		offset_iterator operator--(int) { offset_iterator prev = *this; --(*this); return prev; }

		// TODO: operator +/- if underlying iterator supports it
		// TODO: relational operators

		auto base() const
		{
			assert(offset <= array.size()); // ok to form iterator one past end
			assert(offset >= 0);
			using std::begin;
			return begin(array) + offset;
		}

		auto&& operator*() const
		{
			assert(offset != array.size()); // but not ok to deref it
			return *base();
		}

	};
	template <typename A> offset_iterator(A&&, typename offset_iterator<A>::offset_type) -> offset_iterator<A>;
	template <typename A> offset_iterator(A&&, typename offset_iterator<A>::iterator_type) -> offset_iterator<A>;
	template <typename A> offset_iterator(A&&) -> offset_iterator<A>;
	// TODO offset_iterator based on another iterator, not range, it basically just counts the ++ and --, while also moving the underlying iterator, allows keeping track of size of the relevant ranges with forward and bidirectional iterators, and for random access might still be useful as an iterator that remembers where it started, very similar to simple/geom/iterator.hpp perhaps just a 1D vectirator

} // namespace simple::support

#endif /* end of include guard */
